/* * $Id$ * $URL$ */ package org.subethamail.web.action.auth; import java.security.Principal; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import lombok.extern.java.Log; import org.apache.commons.codec.binary.Base64; import org.subethamail.core.auth.SubEthaPrincipal; import org.subethamail.web.Backend; import org.subethamail.web.action.SubEthaAction; import org.subethamail.web.security.SubEthaLogin; import com.voodoodyne.tagonist.ActionContext; /** * Provides basic authentication services to action subclasses. * * Unfortunately there are some serious defects in J2EE integrated * security, so we will maintain the "authenticated" state ourselves * by putting a value, PRINCIPAL_KEY, in the user session attrs. * * Some helpful (or not) URLs: * * http://www.jboss.org/index.html?module=bb&op=viewtopic&t=52152 * http://www.jboss.org/index.html?module=bb&op=viewtopic&p=3874200 * http://www.luminis.nl/publications/websecurity.html * * The autologin cookie is a string "username/password" which has * been DES encrypted and Base64 encoded. * * @author Jeff Schnitzer */ @Log abstract public class AuthAction extends SubEthaAction { /** Name of autologin cookie */ protected static final String AUTO_LOGIN_COOKIE_KEY = "subetha.auth"; /** The j2ee security role for administrative users */ public final static String SITE_ADMIN_ROLE = "siteAdmin"; /** * Actually perform the login logic by calling into the JAAS stack. * * @throws LoginException if it didn't work. */ public void login(String who, String password) throws LoginException { SubEthaLogin rl = Backend.instance().getLogin(); rl.logout(this.getCtx().getRequest()); log.log(Level.FINE,"Successful authentication for: {0}", who); if (!rl.login(who, password, this.getCtx().getRequest())) throw new FailedLoginException("Bad username or password"); } /** * @return true if the user has been authenticated. */ public boolean isLoggedIn() { SubEthaPrincipal prin = this.getPrincipal(); log.log(Level.FINE,"isLoggedIn={0}!=null",prin); boolean res = prin != null; return res; } /** * @return whether or not the user is the most powerful kind of administrator */ public boolean isSiteAdmin() { return this.getCtx().getRequest().isUserInRole(SITE_ADMIN_ROLE); } /** * @return the email of the currently logged-in user, or null if not logged in. */ public String getAuthName() { if (this.isLoggedIn()) return getPrincipal().getEmail(); else return null; } /** * Logs the user out. */ protected void logout() { Backend.instance().getLogin().logout(this.getCtx().getRequest()); } /** * Stops auto-login from working by clearing the cookie credentials. */ protected void stopAutoLogin() { this.setCookie(AUTO_LOGIN_COOKIE_KEY, "", 0); } /** */ protected void setAutoLogin(String name, String password) throws Exception { this.setCookie(AUTO_LOGIN_COOKIE_KEY, this.encryptAutoLogin(name, password), Integer.MAX_VALUE); } /** * Tries to execute an autologin. Not guaranteed to work, in which case nothing happens. * Side effects might include the user being logged in, or an invalid autlogin cookie * being deleted. */ protected void tryAutoLogin() throws Exception { Cookie cook = this.getCookie(AUTO_LOGIN_COOKIE_KEY); if (cook != null) { String[] nameAndPass = this.decryptAutoLogin(cook.getValue()); if (nameAndPass != null) { try { this.login(nameAndPass[0], nameAndPass[1]); } catch (LoginException ex) { this.stopAutoLogin(); } } } } /** * The cookie string is "name/password" but with both parts URLEncoded first. * * @return a value suitable for a cookie. */ protected String encryptAutoLogin(String name, String password) throws Exception { List<String> pair = new ArrayList<String>(2); pair.add(name); pair.add(password); byte[] cipherText = Backend.instance().getEncryptor().encryptList(pair); return new String(Base64.encodeBase64(cipherText)); } /** * @param cookieText was encrypted with encryptAutoLogin() * @return the cookie translated into username (index 0) and password (index 1), * or null if anything at all went wrong. */ protected String[] decryptAutoLogin(String cookieText) { try { byte[] cipherText = Base64.decodeBase64(cookieText.getBytes()); List<String> pair = Backend.instance().getEncryptor().decryptList(cipherText); return new String[] { pair.get(0), pair.get(1) }; } catch (Exception ex) { log.log(Level.FINE,"Error decrypting autologin cookie: ", ex); // Delete the damn thing this.stopAutoLogin(); return null; } } /** * helper method to consolidate {@link Principal} acquisition * @return the current {@link Principal} */ protected SubEthaPrincipal getPrincipal() { ActionContext ctx = this.getCtx(); log.log(Level.FINE,"ActionContext={0}",ctx); if (ctx==null) return null; HttpServletRequest request = ctx.getRequest(); log.log(Level.FINE,"HttpServletRequest={0}",request); if (request==null) return null; Principal p = request.getUserPrincipal(); log.log(Level.FINE,"Principal={0}",p); if (p!=null && p instanceof SubEthaPrincipal) return (SubEthaPrincipal)p; return null; } }